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 fmt::Debug,
48 ops::{Deref, DerefMut},
49};
50
51struct BresenhamLineIter {
52 dx: i32,
53 dy: i32,
54 x: i32,
55 y: i32,
56 error: i32,
57 end_x: i32,
58 is_steep: bool,
59 y_step: i32,
60}
61
62impl BresenhamLineIter {
63 fn new(start: Vector2<i32>, end: Vector2<i32>) -> BresenhamLineIter {
64 let (mut x0, mut y0) = (start.x, start.y);
65 let (mut x1, mut y1) = (end.x, end.y);
66
67 let is_steep = (y1 - y0).abs() > (x1 - x0).abs();
68 if is_steep {
69 std::mem::swap(&mut x0, &mut y0);
70 std::mem::swap(&mut x1, &mut y1);
71 }
72
73 if x0 > x1 {
74 std::mem::swap(&mut x0, &mut x1);
75 std::mem::swap(&mut y0, &mut y1);
76 }
77
78 let dx = x1 - x0;
79
80 BresenhamLineIter {
81 dx,
82 dy: (y1 - y0).abs(),
83 x: x0,
84 y: y0,
85 error: dx / 2,
86 end_x: x1,
87 is_steep,
88 y_step: if y0 < y1 { 1 } else { -1 },
89 }
90 }
91}
92
93impl Iterator for BresenhamLineIter {
94 type Item = Vector2<i32>;
95
96 fn next(&mut self) -> Option<Vector2<i32>> {
97 if self.x > self.end_x {
98 None
99 } else {
100 let ret = if self.is_steep {
101 Vector2::new(self.y, self.x)
102 } else {
103 Vector2::new(self.x, self.y)
104 };
105
106 self.x += 1;
107 self.error -= self.dy;
108 if self.error < 0 {
109 self.y += self.y_step;
110 self.error += self.dx;
111 }
112
113 Some(ret)
114 }
115 }
116}
117
118#[derive(Clone, Debug, Default)]
120pub struct TileSetUpdate(FxHashMap<TileDefinitionHandle, TileDataUpdate>);
121
122impl Deref for TileSetUpdate {
123 type Target = FxHashMap<TileDefinitionHandle, TileDataUpdate>;
124 fn deref(&self) -> &Self::Target {
125 &self.0
126 }
127}
128impl DerefMut for TileSetUpdate {
129 fn deref_mut(&mut self) -> &mut Self::Target {
130 &mut self.0
131 }
132}
133
134#[derive(Debug, Clone)]
137pub enum MaterialUpdate {
138 Erase,
140 Replace(TileMaterialBounds),
142}
143
144#[derive(Clone, Debug, Default)]
146pub enum TileDataUpdate {
147 #[default]
149 Erase,
150 DoNothing,
152 MaterialTile(TileData),
154 FreeformTile(TileDefinition),
156 TransformSet(Option<TileDefinitionHandle>),
160 Color(Color),
162 Property(Uuid, Option<TileSetPropertyValue>),
164 PropertySlice(Uuid, [Option<i8>; 9]),
166 Collider(FxHashMap<Uuid, TileCollider>),
168 Material(TileMaterialBounds),
170}
171
172impl TileDataUpdate {
173 pub fn apply_to_property_value(
175 &self,
176 property_id: Uuid,
177 value: TileSetPropertyValue,
178 ) -> TileSetPropertyValue {
179 match self {
180 TileDataUpdate::Erase => value.make_default(),
181 TileDataUpdate::DoNothing => value,
182 TileDataUpdate::MaterialTile(tile_data) => tile_data
183 .properties
184 .get(&property_id)
185 .cloned()
186 .unwrap_or(value.make_default()),
187 TileDataUpdate::FreeformTile(tile_definition) => tile_definition
188 .data
189 .properties
190 .get(&property_id)
191 .cloned()
192 .unwrap_or(value.make_default()),
193 TileDataUpdate::TransformSet(_) => value,
194 TileDataUpdate::Color(_) => value,
195 TileDataUpdate::Property(uuid, new_value) => {
196 if *uuid == property_id {
197 new_value.as_ref().cloned().unwrap_or(value.make_default())
198 } else {
199 value
200 }
201 }
202 TileDataUpdate::PropertySlice(uuid, data) => match value {
203 TileSetPropertyValue::NineSlice(mut old_data) if property_id == *uuid => {
204 for (i, v) in data.iter().enumerate() {
205 old_data.0[i] = v.unwrap_or(old_data.0[i]);
206 }
207 TileSetPropertyValue::NineSlice(old_data)
208 }
209 _ if property_id == *uuid => {
210 TileSetPropertyValue::NineSlice(NineI8(data.map(|x| x.unwrap_or_default())))
211 }
212 _ => value,
213 },
214 TileDataUpdate::Collider(_) => value,
215 TileDataUpdate::Material(_) => value,
216 }
217 }
218 pub fn get_tile_collider(&self, uuid: &Uuid) -> Option<&TileCollider> {
221 match self {
222 TileDataUpdate::Erase => Some(&TileCollider::None),
223 TileDataUpdate::MaterialTile(data) => {
224 data.colliders.get(uuid).or(Some(&TileCollider::None))
225 }
226 TileDataUpdate::FreeformTile(def) => {
227 def.data.colliders.get(uuid).or(Some(&TileCollider::None))
228 }
229 TileDataUpdate::Collider(map) => map.get(uuid),
230 _ => None,
231 }
232 }
233 pub fn substitute_transform_handle(
238 &self,
239 source: TileDefinitionHandle,
240 ) -> Option<TileDefinitionHandle> {
241 if let TileDataUpdate::TransformSet(new_source) = self {
242 *new_source
243 } else {
244 Some(source)
245 }
246 }
247 pub fn modify_render<'a>(&self, source: &'a TileRenderData) -> Option<Cow<'a, TileRenderData>> {
250 match self {
251 TileDataUpdate::Erase => None,
252 TileDataUpdate::MaterialTile(tile_data) => Some(Cow::Owned(TileRenderData::new(
253 source.material_bounds.clone(),
254 tile_data.color,
255 ))),
256 TileDataUpdate::FreeformTile(def) => Some(Cow::Owned(TileRenderData::new(
257 Some(def.material_bounds.clone()),
258 def.data.color,
259 ))),
260 TileDataUpdate::Color(color) => Some(Cow::Owned(TileRenderData::new(
261 source.material_bounds.clone(),
262 *color,
263 ))),
264 TileDataUpdate::Material(material_bounds) => Some(Cow::Owned(TileRenderData::new(
265 Some(material_bounds.clone()),
266 source.color,
267 ))),
268 _ => Some(Cow::Borrowed(source)),
269 }
270 }
271 pub fn take_data(&mut self) -> TileData {
273 match std::mem::take(self) {
274 TileDataUpdate::MaterialTile(d) => d,
275 _ => panic!(),
276 }
277 }
278 pub fn take_definition(&mut self) -> TileDefinition {
280 match std::mem::take(self) {
281 TileDataUpdate::FreeformTile(d) => d,
282 _ => panic!(),
283 }
284 }
285 pub fn swap_with_data(&mut self, data: &mut TileData) {
288 match self {
289 TileDataUpdate::DoNothing => (),
290 TileDataUpdate::Erase => {
291 Log::err("Tile data swap error");
292 *self = Self::DoNothing;
293 }
294 TileDataUpdate::MaterialTile(tile_data) => std::mem::swap(tile_data, data),
295 TileDataUpdate::FreeformTile(tile_definition) => {
296 std::mem::swap(&mut tile_definition.data, data)
297 }
298 TileDataUpdate::Color(color) => std::mem::swap(color, &mut data.color),
299 TileDataUpdate::Collider(colliders) => {
300 for (uuid, value) in colliders.iter_mut() {
301 match data.colliders.entry(*uuid) {
302 Entry::Occupied(mut e) => {
303 if let TileCollider::None = value {
304 *value = e.remove();
305 } else {
306 std::mem::swap(e.get_mut(), value)
307 }
308 }
309 Entry::Vacant(e) => {
310 e.insert(value.clone());
311 *value = TileCollider::None;
312 }
313 }
314 }
315 }
316 TileDataUpdate::Property(uuid, value) => {
317 swap_hash_map_entry(data.properties.entry(*uuid), value)
318 }
319 TileDataUpdate::PropertySlice(uuid, value) => match data.properties.entry(*uuid) {
320 Entry::Occupied(mut e) => {
321 if let TileSetPropertyValue::NineSlice(v0) = e.get_mut() {
322 for (v0, v1) in v0.0.iter_mut().zip(value.iter_mut()) {
323 if let Some(v1) = v1 {
324 std::mem::swap(v0, v1);
325 }
326 }
327 }
328 }
329 Entry::Vacant(e) => {
330 let _ = e.insert(TileSetPropertyValue::NineSlice(NineI8(
331 value.map(|v| v.unwrap_or_default()),
332 )));
333 *self = TileDataUpdate::Property(*uuid, None);
334 }
335 },
336 TileDataUpdate::TransformSet(_) => {
337 Log::err("Tile data swap error");
338 *self = Self::DoNothing;
339 }
340 TileDataUpdate::Material(_) => {
341 Log::err("Tile data swap error");
342 *self = Self::DoNothing;
343 }
344 }
345 }
346}
347
348impl TileSetUpdate {
349 pub fn convert(
356 &mut self,
357 tiles: &TransTilesUpdate,
358 tile_set: &TileSetResource,
359 page: Vector2<i32>,
360 source_set: &TileSetResource,
361 ) {
362 let tile_set = tile_set.data_ref();
363 let Some(page_object) = tile_set.get_page(page) else {
364 return;
365 };
366 match &page_object.source {
367 TileSetPageSource::Atlas(_) => self.convert_material(tiles, page),
368 TileSetPageSource::Freeform(_) => {
369 drop(tile_set);
370 self.convert_freeform(tiles, &TileSetRef::new(source_set).as_loaded(), page);
371 }
372 TileSetPageSource::Transform(_) | TileSetPageSource::Animation(_) => {
373 drop(tile_set);
374 self.convert_transform(tiles, &TileSetRef::new(source_set).as_loaded(), page);
375 }
376 }
377 }
378 fn convert_material(&mut self, tiles: &TransTilesUpdate, page: Vector2<i32>) {
379 for (pos, value) in tiles.iter() {
380 let Some(handle) = TileDefinitionHandle::try_new(page, *pos) else {
381 continue;
382 };
383 if value.is_some() {
384 self.insert(handle, TileDataUpdate::MaterialTile(TileData::default()));
385 } else {
386 self.insert(handle, TileDataUpdate::Erase);
387 }
388 }
389 }
390 fn convert_freeform(
391 &mut self,
392 tiles: &TransTilesUpdate,
393 tile_set: &OptionTileSet,
394 page: Vector2<i32>,
395 ) {
396 for (pos, value) in tiles.iter() {
397 let Some(handle) = TileDefinitionHandle::try_new(page, *pos) else {
398 continue;
399 };
400 if let Some(def) = value
401 .as_ref()
402 .map(|v| v.pair())
403 .and_then(|(t, h)| tile_set.get_transformed_definition(t, h))
404 {
405 self.insert(handle, TileDataUpdate::FreeformTile(def));
406 } else {
407 self.insert(handle, TileDataUpdate::Erase);
408 }
409 }
410 }
411 fn convert_transform(
412 &mut self,
413 tiles: &TransTilesUpdate,
414 tile_set: &OptionTileSet,
415 page: Vector2<i32>,
416 ) {
417 for (pos, value) in tiles.iter() {
418 let Some(target_handle) = TileDefinitionHandle::try_new(page, *pos) else {
419 continue;
420 };
421 if let Some((trans, handle)) = value.as_ref().map(|v| v.pair()) {
422 let handle = tile_set
423 .get_transformed_version(trans, handle)
424 .unwrap_or(handle);
425 self.insert(target_handle, TileDataUpdate::TransformSet(Some(handle)));
426 } else {
427 self.insert(target_handle, TileDataUpdate::TransformSet(None));
428 }
429 }
430 }
431 pub fn get_color(&self, page: Vector2<i32>, position: Vector2<i32>) -> Option<Color> {
433 let handle = TileDefinitionHandle::try_new(page, position)?;
434 match self.get(&handle)? {
435 TileDataUpdate::Erase => Some(Color::default()),
436 TileDataUpdate::MaterialTile(data) => Some(data.color),
437 TileDataUpdate::FreeformTile(def) => Some(def.data.color),
438 TileDataUpdate::Color(color) => Some(*color),
439 _ => None,
440 }
441 }
442 pub fn get_material(
444 &self,
445 page: Vector2<i32>,
446 position: Vector2<i32>,
447 ) -> Option<MaterialUpdate> {
448 let handle = TileDefinitionHandle::try_new(page, position)?;
449 match self.get(&handle)? {
450 TileDataUpdate::Erase => Some(MaterialUpdate::Erase),
451 TileDataUpdate::FreeformTile(def) => {
452 Some(MaterialUpdate::Replace(def.material_bounds.clone()))
453 }
454 TileDataUpdate::Material(mat) => Some(MaterialUpdate::Replace(mat.clone())),
455 _ => None,
456 }
457 }
458 pub fn get_tile_bounds(
460 &self,
461 page: Vector2<i32>,
462 position: Vector2<i32>,
463 ) -> Option<TileBounds> {
464 let handle = TileDefinitionHandle::try_new(page, position)?;
465 match self.get(&handle)? {
466 TileDataUpdate::Erase => Some(TileBounds::default()),
467 TileDataUpdate::FreeformTile(def) => Some(def.material_bounds.bounds.clone()),
468 TileDataUpdate::Material(mat) => Some(mat.bounds.clone()),
469 _ => None,
470 }
471 }
472 pub fn get_property(
474 &self,
475 page: Vector2<i32>,
476 position: Vector2<i32>,
477 property_id: Uuid,
478 ) -> Option<Option<TileSetPropertyValue>> {
479 let handle = TileDefinitionHandle::try_new(page, position)?;
480 match self.get(&handle)? {
481 TileDataUpdate::Erase => Some(None),
482 TileDataUpdate::MaterialTile(data) => Some(data.properties.get(&property_id).cloned()),
483 TileDataUpdate::FreeformTile(def) => {
484 Some(def.data.properties.get(&property_id).cloned())
485 }
486 TileDataUpdate::Property(id, value) if *id == property_id => Some(value.clone()),
487 _ => None,
488 }
489 }
490 pub fn get_collider(
492 &self,
493 page: Vector2<i32>,
494 position: Vector2<i32>,
495 collider_id: &Uuid,
496 ) -> Option<&TileCollider> {
497 let handle = TileDefinitionHandle::try_new(page, position)?;
498 self.get(&handle)?.get_tile_collider(collider_id)
499 }
500 pub fn set_color(&mut self, page: Vector2<i32>, position: Vector2<i32>, color: Color) {
502 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
503 self.insert(handle, TileDataUpdate::Color(color));
504 }
505 }
506 pub fn set_property(
508 &mut self,
509 page: Vector2<i32>,
510 position: Vector2<i32>,
511 property_id: Uuid,
512 value: Option<TileSetPropertyValue>,
513 ) {
514 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
515 self.insert(handle, TileDataUpdate::Property(property_id, value));
516 }
517 }
518 pub fn set_property_slice(
520 &mut self,
521 page: Vector2<i32>,
522 position: Vector2<i32>,
523 subposition: Vector2<usize>,
524 property_id: Uuid,
525 value: i8,
526 ) {
527 use TileSetPropertyValue as PropValue;
528 let index = TileSetPropertyValue::nine_position_to_index(subposition);
529 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
530 match self.entry(handle) {
531 Entry::Occupied(mut e) => match e.get_mut() {
532 TileDataUpdate::PropertySlice(uuid, d0) if *uuid == property_id => {
533 d0[index] = Some(value);
534 }
535 TileDataUpdate::Property(uuid, Some(PropValue::NineSlice(d0)))
536 if *uuid == property_id =>
537 {
538 d0.0[index] = value;
539 }
540 d0 => {
541 let mut data = [0; 9];
542 data[index] = value;
543 *d0 = TileDataUpdate::Property(
544 property_id,
545 Some(PropValue::NineSlice(NineI8(data))),
546 );
547 }
548 },
549 Entry::Vacant(e) => {
550 let mut data = [None; 9];
551 data[index] = Some(value);
552 let _ = e.insert(TileDataUpdate::PropertySlice(property_id, data));
553 }
554 }
555 }
556 }
557 pub fn set_collider<I: Iterator<Item = Uuid>>(
559 &mut self,
560 page: Vector2<i32>,
561 position: Vector2<i32>,
562 property_ids: I,
563 value: &TileCollider,
564 ) {
565 let Some(handle) = TileDefinitionHandle::try_new(page, position) else {
566 return;
567 };
568 let mut colliders = FxHashMap::default();
569 colliders.extend(property_ids.map(|uuid| (uuid, value.clone())));
570 self.insert(handle, TileDataUpdate::Collider(colliders));
571 }
572 pub fn set_material(
574 &mut self,
575 page: Vector2<i32>,
576 position: Vector2<i32>,
577 value: TileMaterialBounds,
578 ) {
579 if let Some(handle) = TileDefinitionHandle::try_new(page, position) {
580 self.insert(handle, TileDataUpdate::Material(value));
581 }
582 }
583}
584
585#[derive(Debug, Clone, Reflect)]
589pub struct RotTileHandle {
590 pub transform: OrthoTransformation,
592 pub element: StampElement,
594}
595
596impl RotTileHandle {
597 pub fn pair(&self) -> (OrthoTransformation, TileDefinitionHandle) {
599 (self.transform, self.element.handle)
600 }
601}
602
603#[derive(Clone, Debug, Default)]
607pub struct TransTilesUpdate(TileGridMap<Option<RotTileHandle>>);
608
609#[derive(Clone, Default)]
614pub struct MacroTilesUpdate(TileGridMap<Option<StampElement>>);
615
616impl Debug for MacroTilesUpdate {
617 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
618 f.write_str("MacroTilesUpdate")?;
619 for (p, v) in self.0.iter() {
620 write!(f, " ({:2},{:2})->", p.x, p.y)?;
621 if let Some(StampElement { handle, source }) = v {
622 write!(f, "{handle}")?;
623 if let Some(cell) = source {
624 write!(f, "[{cell}]")?;
625 }
626 } else {
627 f.write_str("Delete")?;
628 }
629 }
630 Ok(())
631 }
632}
633
634#[derive(Clone, Debug, Default, PartialEq)]
637pub struct TilesUpdate(TileGridMap<Option<TileDefinitionHandle>>);
638
639impl Deref for TilesUpdate {
640 type Target = TileGridMap<Option<TileDefinitionHandle>>;
641
642 fn deref(&self) -> &Self::Target {
643 &self.0
644 }
645}
646
647impl DerefMut for TilesUpdate {
648 fn deref_mut(&mut self) -> &mut Self::Target {
649 &mut self.0
650 }
651}
652
653impl Deref for MacroTilesUpdate {
654 type Target = TileGridMap<Option<StampElement>>;
655
656 fn deref(&self) -> &Self::Target {
657 &self.0
658 }
659}
660
661impl DerefMut for MacroTilesUpdate {
662 fn deref_mut(&mut self) -> &mut Self::Target {
663 &mut self.0
664 }
665}
666
667impl Deref for TransTilesUpdate {
668 type Target = TileGridMap<Option<RotTileHandle>>;
669
670 fn deref(&self) -> &Self::Target {
671 &self.0
672 }
673}
674
675impl DerefMut for TransTilesUpdate {
676 fn deref_mut(&mut self) -> &mut Self::Target {
677 &mut self.0
678 }
679}
680
681impl MacroTilesUpdate {
682 pub fn build_tiles_update(&self) -> TilesUpdate {
684 let mut result = TilesUpdate::default();
685 for (pos, value) in self.iter() {
686 let handle = value.as_ref().map(|e| e.handle);
687 result.insert(*pos, handle);
688 }
689 result
690 }
691 pub fn fill_trans_tiles_update(&self, tiles_update: &mut TransTilesUpdate) {
694 tiles_update.clear();
695 for (pos, tile) in self.iter() {
696 let value = tile.as_ref().cloned().map(|element| RotTileHandle {
697 transform: OrthoTransformation::identity(),
698 element,
699 });
700 tiles_update.insert(*pos, value);
701 }
702 }
703}
704
705impl TransTilesUpdate {
706 pub fn build_tiles_update(&self, tile_set: &OptionTileSet) -> TilesUpdate {
709 let mut result = TilesUpdate::default();
710 for (pos, value) in self.iter() {
711 if let Some((trans, handle)) = value.as_ref().map(|v| v.pair()) {
712 let handle = tile_set
713 .get_transformed_version(trans, handle)
714 .unwrap_or(handle);
715 result.insert(*pos, Some(handle));
716 } else {
717 result.insert(*pos, None);
718 }
719 }
720 result
721 }
722 pub fn fill_macro_tiles_update(
725 &self,
726 tile_set: &OptionTileSet,
727 macro_update: &mut MacroTilesUpdate,
728 ) {
729 macro_update.clear();
730 for (pos, value) in self.iter() {
731 if let Some(RotTileHandle { transform, element }) = value.as_ref() {
732 let handle = element.handle;
733 let handle = tile_set
734 .get_transformed_version(*transform, handle)
735 .unwrap_or(handle);
736 macro_update.insert(
737 *pos,
738 Some(StampElement {
739 handle,
740 source: element.source,
741 }),
742 );
743 } else {
744 macro_update.insert(*pos, None);
745 }
746 }
747 }
748 pub fn flood_fill<T: BoundedTileSource, S: TileSource>(
754 &mut self,
755 tiles: &T,
756 start_point: Vector2<i32>,
757 brush: &S,
758 ) {
759 let mut bounds = tiles.bounding_rect();
760 bounds.push(start_point);
761
762 let allowed_definition = tiles.get_at(start_point).map(|t| t.handle);
763 let mut stack = vec![start_point];
764 while let Some(position) = stack.pop() {
765 let definition = tiles.get_at(position).map(|t| t.handle);
766 if definition == allowed_definition && !self.contains_key(&position) {
767 let value = brush.get_at(position).map(|element| RotTileHandle {
768 transform: brush.transformation(),
769 element,
770 });
771 self.insert(position, value);
772
773 for neighbour_position in [
775 Vector2::new(position.x - 1, position.y),
776 Vector2::new(position.x + 1, position.y),
777 Vector2::new(position.x, position.y - 1),
778 Vector2::new(position.x, position.y + 1),
779 ] {
780 if bounds.contains(neighbour_position) {
781 stack.push(neighbour_position);
782 }
783 }
784 }
785 }
786 }
787 #[inline]
789 pub fn draw_tiles(&mut self, origin: Vector2<i32>, brush: &Stamp) {
790 let transform = brush.transformation();
791 for (local_position, element) in brush.iter() {
792 let element = element.clone();
793 self.insert(
794 origin + local_position,
795 Some(RotTileHandle { transform, element }),
796 );
797 }
798 }
799 #[inline]
801 pub fn erase_stamp(&mut self, origin: Vector2<i32>, brush: &Stamp) {
802 for local_position in brush.keys() {
803 self.insert(origin + local_position, None);
804 }
805 }
806 pub fn erase(&mut self, position: Vector2<i32>) {
808 self.insert(position, None);
809 }
810 pub fn rect_fill(&mut self, start: Vector2<i32>, end: Vector2<i32>, stamp: &Stamp) {
812 let region = TileRegion::from_points(start, end);
813 let stamp_source = stamp.repeat(start, end);
814 self.rect_fill_inner(region, &stamp_source);
815 }
816 pub fn rect_fill_random(&mut self, start: Vector2<i32>, end: Vector2<i32>, stamp: &Stamp) {
818 let region = TileRegion::from_points(start, end);
819 self.rect_fill_inner(region, &RandomTileSource(stamp));
820 }
821 fn rect_fill_inner<S: TileSource>(&mut self, region: TileRegion, brush: &S) {
823 let transform = brush.transformation();
824 for (target, source) in region.iter() {
825 if let Some(element) = brush.get_at(source) {
826 self.insert(target, Some(RotTileHandle { transform, element }));
827 }
828 }
829 }
830 pub fn draw_line<S: TileSource>(&mut self, from: Vector2<i32>, to: Vector2<i32>, brush: &S) {
832 let transform = brush.transformation();
833 for position in BresenhamLineIter::new(from, to) {
834 if let Some(element) = brush.get_at(position - from) {
835 self.insert(position, Some(RotTileHandle { transform, element }));
836 }
837 }
838 }
839
840 pub fn nine_slice(&mut self, start: Vector2<i32>, end: Vector2<i32>, brush: &Stamp) {
844 self.nine_slice_inner(
845 start,
846 end,
847 brush,
848 |update, target_region, source, source_region| {
849 update.rect_fill_inner(
850 target_region,
851 &RepeatTileSource {
852 source,
853 region: source_region,
854 },
855 )
856 },
857 );
858 }
859 pub fn nine_slice_random(&mut self, start: Vector2<i32>, end: Vector2<i32>, brush: &Stamp) {
863 self.nine_slice_inner(
864 start,
865 end,
866 brush,
867 |update, target_region, source, source_region| {
868 update.rect_fill_inner(
869 target_region,
870 &PartialRandomTileSource(source, source_region.bounds),
871 )
872 },
873 );
874 }
875
876 #[inline]
880 fn nine_slice_inner<F>(
881 &mut self,
882 start: Vector2<i32>,
883 end: Vector2<i32>,
884 stamp: &Stamp,
885 fill: F,
886 ) where
887 F: Fn(&mut TransTilesUpdate, TileRegion, &Stamp, TileRegion),
888 {
889 let Some(stamp_rect) = *stamp.bounding_rect() else {
890 return;
891 };
892 let rect = TileRect::from_points(start, end);
893 let region = TileRegion {
894 origin: start,
895 bounds: rect.into(),
896 };
897 let inner_region = region.clone().deflate(1, 1);
898
899 let stamp_region = TileRegion::from_bounds_and_direction(stamp_rect.into(), start - end);
900 let inner_stamp_region = stamp_region.clone().deflate(1, 1);
901
902 let transform = stamp.transformation();
904 for (corner_position, actual_corner_position) in [
905 (stamp_rect.left_top_corner(), rect.left_top_corner()),
906 (stamp_rect.right_top_corner(), rect.right_top_corner()),
907 (stamp_rect.right_bottom_corner(), rect.right_bottom_corner()),
908 (stamp_rect.left_bottom_corner(), rect.left_bottom_corner()),
909 ] {
910 if let Some(element) = stamp.get(corner_position).cloned() {
911 self.insert(
912 actual_corner_position,
913 Some(RotTileHandle { transform, element }),
914 );
915 }
916 }
917
918 let top = region.clone().with_bounds(
919 TileRect::from_points(
920 rect.left_top_corner() + Vector2::new(1, 0),
921 rect.right_top_corner() + Vector2::new(-1, 0),
922 )
923 .into(),
924 );
925 let bottom = region.clone().with_bounds(
926 TileRect::from_points(
927 rect.left_bottom_corner() + Vector2::new(1, 0),
928 rect.right_bottom_corner() + Vector2::new(-1, 0),
929 )
930 .into(),
931 );
932 let left = region.clone().with_bounds(
933 TileRect::from_points(
934 rect.left_bottom_corner() + Vector2::new(0, 1),
935 rect.left_top_corner() + Vector2::new(0, -1),
936 )
937 .into(),
938 );
939 let right = region.clone().with_bounds(
940 TileRect::from_points(
941 rect.right_bottom_corner() + Vector2::new(0, 1),
942 rect.right_top_corner() + Vector2::new(0, -1),
943 )
944 .into(),
945 );
946 let stamp_top = stamp_region.clone().with_bounds(
947 TileRect::from_points(
948 stamp_rect.left_top_corner() + Vector2::new(1, 0),
949 stamp_rect.right_top_corner() + Vector2::new(-1, 0),
950 )
951 .into(),
952 );
953 let stamp_bottom = stamp_region.clone().with_bounds(
954 TileRect::from_points(
955 stamp_rect.left_bottom_corner() + Vector2::new(1, 0),
956 stamp_rect.right_bottom_corner() + Vector2::new(-1, 0),
957 )
958 .into(),
959 );
960 let stamp_left = stamp_region.clone().with_bounds(
961 TileRect::from_points(
962 stamp_rect.left_bottom_corner() + Vector2::new(0, 1),
963 stamp_rect.left_top_corner() + Vector2::new(0, -1),
964 )
965 .into(),
966 );
967 let stamp_right = stamp_region.clone().with_bounds(
968 TileRect::from_points(
969 stamp_rect.right_bottom_corner() + Vector2::new(0, 1),
970 stamp_rect.right_top_corner() + Vector2::new(0, -1),
971 )
972 .into(),
973 );
974
975 if rect.size.x > 2 && stamp_rect.size.x > 2 {
976 fill(self, top, stamp, stamp_top);
977 fill(self, bottom, stamp, stamp_bottom);
978 }
979 if rect.size.y > 2 && stamp_rect.size.y > 2 {
980 fill(self, left, stamp, stamp_left);
981 fill(self, right, stamp, stamp_right);
982 }
983 fill(self, inner_region, stamp, inner_stamp_region);
984 }
985}