Skip to main content

fyrox_impl/scene/tilemap/
property.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! A property layer allows a tile set to store arbitrary values along with each tile
22//! in a tile set. A tile may have an integer, a float, or a string that the game may
23//! use to recognize special properties of the tile. These properties can be accessed
24//! using either the property name or the property UUID.
25//!
26//! See [`TileSetPropertyValue`] for the possible value types.
27//!
28//! A property layer may also have a list of pre-defined values which can be named
29//! to specify special meanings to particular values for the property.
30//!
31//! In addition to property layers, there are also collider layers which work much
32//! like property layers, but instead of storing arbitrary data, a collider layer
33//! associates each tile with a shape made from triangles. Each collider layer
34//! has a color that will be used to render the shape in the tile set editor,
35//! so the user can see each tile's shape and the shepe's layer at a glance.
36
37use crate::core::{
38    algebra::Vector2, color::Color, num_traits::Euclid, reflect::prelude::*,
39    type_traits::prelude::*, visitor::prelude::*, ImmutableString,
40};
41use std::fmt::{Debug, Display, Formatter};
42
43use super::*;
44use tileset::*;
45
46/// Trait for objects that identify a tile set property of a particular type.
47pub trait TileSetPropertyId {
48    /// The type of the values of the property.
49    type Property: TryFrom<TileSetPropertyValue, Error = TilePropertyError> + Default;
50    /// The UUID of the property.
51    fn property_uuid(&self) -> &Uuid;
52    /// The value of the property at the given cell of the given tile map.
53    fn get_from_tile_map(
54        &self,
55        tile_map: &TileMap,
56        position: Vector2<i32>,
57    ) -> Result<Self::Property, TilePropertyError> {
58        tile_map.tile_property_value(position, *self.property_uuid())
59    }
60    /// The value of the property at the given handle in the given tile set.
61    fn get_from_tile_set(
62        &self,
63        tile_set: &TileSet,
64        handle: TileDefinitionHandle,
65    ) -> Result<Self::Property, TilePropertyError> {
66        tile_set.tile_property_value(handle, *self.property_uuid())
67    }
68}
69
70/// UUID for a property with values of type i32.
71#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
72pub struct TileSetPropertyI32(pub Uuid);
73/// UUID for a property with values of type f32.
74#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
75pub struct TileSetPropertyF32(pub Uuid);
76/// UUID for a property with values of type ImmutableString.
77#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
78pub struct TileSetPropertyString(pub Uuid);
79/// UUID for a property with values of type [`NineI8`].
80#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
81pub struct TileSetPropertyNine(pub Uuid);
82
83impl TileSetPropertyId for TileSetPropertyI32 {
84    type Property = i32;
85    fn property_uuid(&self) -> &Uuid {
86        &self.0
87    }
88}
89
90impl TileSetPropertyId for TileSetPropertyF32 {
91    type Property = f32;
92    fn property_uuid(&self) -> &Uuid {
93        &self.0
94    }
95}
96
97impl TileSetPropertyId for TileSetPropertyString {
98    type Property = ImmutableString;
99    fn property_uuid(&self) -> &Uuid {
100        &self.0
101    }
102}
103
104impl TileSetPropertyId for TileSetPropertyNine {
105    type Property = NineI8;
106    fn property_uuid(&self) -> &Uuid {
107        &self.0
108    }
109}
110
111/// Since a tile map may require multiple colliders to represent the diverse ways that physics objects may interact with the tiles,
112/// tile set data must allow each tile to include multiple values for its collider information.
113/// These multiple collider values are associated with their collider objects by a UUID and a name.
114#[derive(Clone, Default, Debug, Reflect, Visit)]
115pub struct TileSetColliderLayer {
116    /// The id number that identifies the collider
117    pub uuid: Uuid,
118    /// The name of the collider
119    pub name: ImmutableString,
120    /// The color that will be used to represent the collider in the editor.
121    pub color: Color,
122}
123
124/// In order to allow tile properties to be easily edited, properties need to have consistent names and data types
125/// across all tiles in a tile set. A tile property layer represents the association between a property name
126/// and its data type, along with other information.
127#[derive(Clone, Default, Debug, Reflect, Visit)]
128pub struct TileSetPropertyLayer {
129    /// The id number that identifies this property
130    pub uuid: Uuid,
131    /// The name of the property that will be shown in the editor and can be used access the value.
132    pub name: ImmutableString,
133    /// The data type
134    pub prop_type: TileSetPropertyType,
135    /// Pre-defined named values.
136    pub named_values: Vec<NamedValue>,
137}
138
139/// A value with an associated name. Often certain property values will have special meanings
140/// for the game that is using the values, so it is useful to be able to label those values
141/// so their special meaning can be visible in the editor.
142#[derive(Clone, Default, Debug, Reflect, Visit)]
143pub struct NamedValue {
144    /// The label associated with the value.
145    pub name: String,
146    /// The special value that is being named.
147    pub value: NamableValue,
148    /// The color to represent this value in the editor
149    pub color: Color,
150}
151
152/// Named values can be either an integer or a float.
153/// It would make little sense to name a string or a nine slice.
154#[derive(Copy, Clone, Debug, Reflect, Visit, PartialEq)]
155pub enum NamableValue {
156    /// A value for an element of a nine-slice
157    I8(i8),
158    /// An integer value
159    I32(i32),
160    /// A float value
161    F32(f32),
162}
163
164impl Display for NamableValue {
165    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
166        match self {
167            Self::I8(value) => write!(f, "{value}"),
168            Self::I32(value) => write!(f, "{value}"),
169            Self::F32(value) => write!(f, "{value}"),
170        }
171    }
172}
173
174impl Default for NamableValue {
175    fn default() -> Self {
176        Self::I32(0)
177    }
178}
179
180impl From<NamableValue> for TileSetPropertyValueElement {
181    fn from(value: NamableValue) -> Self {
182        match value {
183            NamableValue::I8(v) => Self::I8(v),
184            NamableValue::I32(v) => Self::I32(v),
185            NamableValue::F32(v) => Self::F32(v),
186        }
187    }
188}
189
190impl TryFrom<TileSetPropertyValueElement> for NamableValue {
191    type Error = ();
192
193    fn try_from(value: TileSetPropertyValueElement) -> Result<Self, ()> {
194        match value {
195            TileSetPropertyValueElement::I32(v) => Ok(Self::I32(v)),
196            TileSetPropertyValueElement::F32(v) => Ok(Self::F32(v)),
197            TileSetPropertyValueElement::I8(v) => Ok(Self::I8(v)),
198            TileSetPropertyValueElement::String(_) => Err(()),
199        }
200    }
201}
202
203impl NamableValue {
204    /// True if this value corresponds to the given property value.
205    pub fn matches(&self, other: &TileSetPropertyOptionValue) -> bool {
206        match (self, other) {
207            (Self::I32(x), TileSetPropertyOptionValue::I32(Some(y))) => *x == *y,
208            (Self::F32(x), TileSetPropertyOptionValue::F32(Some(y))) => *x == *y,
209            _ => false,
210        }
211    }
212}
213
214impl TileSetPropertyLayer {
215    /// Find the name associated with the given value.
216    pub fn value_to_name(&self, value: NamableValue) -> String {
217        self.named_values
218            .iter()
219            .find(|v| v.value == value)
220            .map(|v| v.name.clone())
221            .unwrap_or_else(|| format!("{value}"))
222    }
223    /// Find the color associated with the given value.
224    pub fn value_to_color(&self, value: NamableValue) -> Option<Color> {
225        self.named_values
226            .iter()
227            .find(|v| v.value == value)
228            .map(|v| v.color)
229    }
230    /// Return the index of the named value that matches the given value, if one exits.
231    pub fn find_value_index_from_property(
232        &self,
233        value: &TileSetPropertyOptionValue,
234    ) -> Option<usize> {
235        self.named_values
236            .iter()
237            .position(|v| v.value.matches(value))
238    }
239    /// Return the index of the named value that matches the given value, if one exits.
240    pub fn find_value_index(&self, value: NamableValue) -> Option<usize> {
241        self.named_values.iter().position(|v| v.value == value)
242    }
243    /// Return the appropriate highlight color for the tile at the given position when the
244    /// tile has the given property value and the user has selected the given element value.
245    /// If the value does not have a specified highlight color within this layer, then
246    /// the value is compared against the element value and it is given a highlight color
247    /// to acknowledge that the value matches the element value.
248    pub fn highlight_color(
249        &self,
250        position: Vector2<usize>,
251        value: &TileSetPropertyValue,
252        element_value: &TileSetPropertyValueElement,
253    ) -> Option<Color> {
254        use TileSetPropertyValue as PropValue;
255        use TileSetPropertyValueElement as Element;
256        if position != Vector2::new(1, 1) && !matches!(value, PropValue::NineSlice(_)) {
257            return None;
258        }
259        match (value, element_value) {
260            (&PropValue::I32(v0), &Element::I32(v1)) => {
261                self.value_to_color(NamableValue::I32(v0)).or({
262                    if v0 == v1 {
263                        Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
264                    } else {
265                        None
266                    }
267                })
268            }
269            (&PropValue::F32(v0), &Element::F32(v1)) => {
270                self.value_to_color(NamableValue::F32(v0)).or({
271                    if v0 == v1 {
272                        Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
273                    } else {
274                        None
275                    }
276                })
277            }
278            (PropValue::String(v0), Element::String(v1)) => {
279                if v0 == v1 {
280                    Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
281                } else {
282                    None
283                }
284            }
285            (PropValue::NineSlice(v0), &Element::I8(v1)) => {
286                let v = v0.value_at(position);
287                self.value_to_color(NamableValue::I8(v)).or({
288                    if v == v1 {
289                        Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
290                    } else {
291                        None
292                    }
293                })
294            }
295            _ => None,
296        }
297    }
298}
299
300/// Each tile property needs to store a value to indicate what type of data will
301/// be stored in that property, as the data type will affect how the editor
302/// allows users to edit the property on each tile.
303#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Reflect, Visit)]
304pub enum TileSetPropertyType {
305    /// The type for integer properties.
306    #[default]
307    I32,
308    /// The type for float properties.
309    F32,
310    /// The type for string properties.
311    String,
312    /// Nine-slice properties allow a tile to have nine separate values,
313    /// one value for each of its corners, each of its edges, and its center.
314    NineSlice,
315}
316
317impl TileSetPropertyType {
318    /// The default value for properties of the given type.
319    pub fn default_value(&self) -> TileSetPropertyValue {
320        use TileSetPropertyType as PropType;
321        use TileSetPropertyValue as PropValue;
322        match self {
323            PropType::I32 => PropValue::I32(0),
324            PropType::F32 => PropValue::F32(0.0),
325            PropType::String => PropValue::String(ImmutableString::default()),
326            PropType::NineSlice => PropValue::NineSlice(NineI8::default()),
327        }
328    }
329    /// The none value when no value is available.
330    pub fn default_option_value(&self) -> TileSetPropertyOptionValue {
331        use TileSetPropertyOptionValue as PropValue;
332        use TileSetPropertyType as PropType;
333        match self {
334            PropType::I32 => PropValue::I32(None),
335            PropType::F32 => PropValue::F32(None),
336            PropType::String => PropValue::String(None),
337            PropType::NineSlice => PropValue::NineSlice([None; 9]),
338        }
339    }
340}
341
342/// The data stored in a tile property.
343#[derive(Clone, Debug, PartialEq, Reflect, Visit)]
344pub enum TileSetPropertyValue {
345    /// Integer property data.
346    I32(i32),
347    /// Float property data.
348    F32(f32),
349    /// String property data.
350    String(ImmutableString),
351    /// Nine-slice properties allow a tile to have nine separate values,
352    /// one value for each of its corners, each of its edges, and its center.
353    NineSlice(NineI8),
354}
355
356/// Storing a slice of 9 values in a tile is critical to some automatic tiling algorithms
357/// that need to be able identify the content of the edges, corners, or center of each tile.
358/// [Wang tiles](https://en.wikipedia.org/wiki/Wang_tile) are an example of this, where each edge of each tile
359/// is assigned a color and tiles are arranged so that whenever two tiles are adjacent the touching edges
360/// are the same color.
361#[derive(Default, Clone, Copy, PartialEq, Reflect)]
362pub struct NineI8(pub [i8; 9]);
363
364impl Visit for NineI8 {
365    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
366        self.0.visit(name, visitor)
367    }
368}
369
370impl Debug for NineI8 {
371    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
372        let [v0, v1, v2, v3, v4, v5, v6, v7, v8] = self.0;
373        write!(f, "NineI8[{v0} {v1} {v2}/{v3} {v4} {v5}/{v6} {v7} {v8}]")
374    }
375}
376
377impl Display for NineI8 {
378    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
379        let [v0, v1, v2, v3, v4, v5, v6, v7, v8] = self.0;
380        write!(f, "[{v0} {v1} {v2}/{v3} {v4} {v5}/{v6} {v7} {v8}]")
381    }
382}
383
384impl From<[i8; 9]> for NineI8 {
385    fn from(value: [i8; 9]) -> Self {
386        NineI8(value)
387    }
388}
389
390impl From<NineI8> for [i8; 9] {
391    fn from(value: NineI8) -> Self {
392        value.0
393    }
394}
395
396impl NineI8 {
397    /// The value at the given position, with (1,1) being the center of the tile.
398    /// (1,2) represents the top edge of the tile.
399    /// (0,1) represents the left edge of the tile.
400    /// (2,1) represents the right edge of the tile.
401    /// (1,0) represents the bottom edge of the tile.
402    /// Other positions represent the four corners of the tile.
403    pub fn value_at(&self, position: Vector2<usize>) -> i8 {
404        let index = TileSetPropertyValue::nine_position_to_index(position);
405        self.0[index]
406    }
407    /// The value at the given position, with (1,1) being the center of the tile.
408    /// (1,2) represents the top edge of the tile.
409    /// (0,1) represents the left edge of the tile.
410    /// (2,1) represents the right edge of the tile.
411    /// (1,0) represents the bottom edge of the tile.
412    /// Other positions represent the four corners of the tile.
413    pub fn value_at_mut(&mut self, position: Vector2<usize>) -> &mut i8 {
414        let index = TileSetPropertyValue::nine_position_to_index(position);
415        &mut self.0[index]
416    }
417    /// Swap the values at two positions in the slice.
418    pub fn swap(&mut self, a: Vector2<usize>, b: Vector2<usize>) {
419        let a_index = TileSetPropertyValue::nine_position_to_index(a);
420        let b_index = TileSetPropertyValue::nine_position_to_index(b);
421        self.0.swap(a_index, b_index);
422    }
423}
424
425/// An element of data stored within a tile's property.
426/// For most value types, the element is the whole of the value,
427/// but a nine-slice value contains nine elements.
428#[derive(Clone, Debug, PartialEq, Visit, Reflect)]
429pub enum TileSetPropertyValueElement {
430    /// Integer property data.
431    I32(i32),
432    /// Float property data.
433    F32(f32),
434    /// String property data.
435    String(ImmutableString),
436    /// Nine-slice property element.
437    I8(i8),
438}
439
440impl Default for TileSetPropertyValue {
441    fn default() -> Self {
442        Self::I32(0)
443    }
444}
445
446impl Default for TileSetPropertyValueElement {
447    fn default() -> Self {
448        Self::I32(0)
449    }
450}
451
452impl TileSetPropertyValueElement {
453    /// The type of the data in this element.
454    pub fn prop_type(&self) -> TileSetPropertyType {
455        match self {
456            TileSetPropertyValueElement::I32(_) => TileSetPropertyType::I32,
457            TileSetPropertyValueElement::F32(_) => TileSetPropertyType::F32,
458            TileSetPropertyValueElement::String(_) => TileSetPropertyType::String,
459            TileSetPropertyValueElement::I8(_) => TileSetPropertyType::NineSlice,
460        }
461    }
462}
463
464impl OrthoTransform for TileSetPropertyValue {
465    fn x_flipped(self) -> Self {
466        if let Self::NineSlice(mut v) = self {
467            fn pos(x: usize, y: usize) -> Vector2<usize> {
468                Vector2::new(x, y)
469            }
470            v.swap(pos(2, 0), pos(0, 0));
471            v.swap(pos(2, 1), pos(0, 1));
472            v.swap(pos(2, 2), pos(0, 2));
473            Self::NineSlice(v)
474        } else {
475            self
476        }
477    }
478
479    fn rotated(self, amount: i8) -> Self {
480        if let Self::NineSlice(mut v) = self {
481            let amount = amount.rem_euclid(4);
482            nine_rotate(&mut v, amount as usize * 2);
483            Self::NineSlice(v)
484        } else {
485            self
486        }
487    }
488}
489
490const fn nine_index(x: usize, y: usize) -> usize {
491    y * 3 + x
492}
493
494const NINE_ROTATE_LIST: [usize; 8] = [
495    nine_index(0, 0),
496    nine_index(1, 0),
497    nine_index(2, 0),
498    nine_index(2, 1),
499    nine_index(2, 2),
500    nine_index(1, 2),
501    nine_index(0, 2),
502    nine_index(0, 1),
503];
504
505fn nine_rotate(nine: &mut NineI8, amount: usize) {
506    let nine = &mut nine.0;
507    let copy = *nine;
508    for i in 0..(8 - amount) {
509        nine[NINE_ROTATE_LIST[i + amount]] = copy[NINE_ROTATE_LIST[i]];
510    }
511    for i in 0..amount {
512        nine[NINE_ROTATE_LIST[i]] = copy[NINE_ROTATE_LIST[8 - amount + i]];
513    }
514}
515
516impl TileSetPropertyValue {
517    /// The default value for property values of this one's type.
518    pub fn make_default(&self) -> TileSetPropertyValue {
519        match self {
520            TileSetPropertyValue::I32(_) => TileSetPropertyValue::I32(0),
521            TileSetPropertyValue::F32(_) => TileSetPropertyValue::F32(0.0),
522            TileSetPropertyValue::String(_) => {
523                TileSetPropertyValue::String(ImmutableString::default())
524            }
525            TileSetPropertyValue::NineSlice(_) => {
526                TileSetPropertyValue::NineSlice(Default::default())
527            }
528        }
529    }
530    /// The type of the data in this value.
531    pub fn prop_type(&self) -> TileSetPropertyType {
532        match self {
533            TileSetPropertyValue::I32(_) => TileSetPropertyType::I32,
534            TileSetPropertyValue::F32(_) => TileSetPropertyType::F32,
535            TileSetPropertyValue::String(_) => TileSetPropertyType::String,
536            TileSetPropertyValue::NineSlice(_) => TileSetPropertyType::NineSlice,
537        }
538    }
539    /// Converts an x,y position into index in 0..9. Both x and y must be within 0..3.
540    #[inline]
541    pub fn nine_position_to_index(position: Vector2<usize>) -> usize {
542        if position.y > 2 || position.x > 2 {
543            panic!("Illegal nine slice position: {position:?}");
544        }
545        position.y * 3 + position.x
546    }
547    /// Converts an index in 0..9 into an x,y position within a tile's nine slice value.
548    #[inline]
549    pub fn index_to_nine_position(index: usize) -> Vector2<usize> {
550        let (y, x) = index.div_rem_euclid(&3);
551        Vector2::new(x, y)
552    }
553    /// Update this value to match the given value, wherever that value is not None.
554    /// Wherever the given value is None, no change is made to this value.
555    pub fn set_from(&mut self, value: &TileSetPropertyOptionValue) {
556        use TileSetPropertyOptionValue as OptValue;
557        use TileSetPropertyValue as PropValue;
558        match (self, value) {
559            (PropValue::I32(x0), OptValue::I32(Some(x1))) => *x0 = *x1,
560            (PropValue::F32(x0), OptValue::F32(Some(x1))) => *x0 = *x1,
561            (PropValue::String(x0), OptValue::String(Some(x1))) => *x0 = x1.clone(),
562            (PropValue::NineSlice(arr0), OptValue::NineSlice(arr1)) => {
563                for (x0, x1) in arr0.0.iter_mut().zip(arr1.iter()) {
564                    if let Some(v) = x1 {
565                        *x0 = *v;
566                    }
567                }
568            }
569            _ => (),
570        }
571    }
572}
573
574/// A representation of data stored in a tile property, or the absence of that data
575/// when the data is unknown.
576#[derive(Clone, Debug, PartialEq, Reflect, Visit)]
577pub enum TileSetPropertyOptionValue {
578    /// Integer property data.
579    I32(Option<i32>),
580    /// Float property data.
581    F32(Option<f32>),
582    /// String property data.
583    String(Option<ImmutableString>),
584    /// Nine-slice properties allow a tile to have nine separate values,
585    /// one value for each of its corners, each of its edges, and its center.
586    NineSlice([Option<i8>; 9]),
587}
588
589impl Default for TileSetPropertyOptionValue {
590    fn default() -> Self {
591        Self::I32(None)
592    }
593}
594
595impl TryFrom<TileSetPropertyValue> for i32 {
596    type Error = TilePropertyError;
597
598    fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
599        use TilePropertyError::*;
600        use TileSetPropertyValue::*;
601        match value {
602            I32(v) => Ok(v),
603            F32(_) => Err(WrongType("Expected: i32, Found: f32")),
604            String(_) => Err(WrongType("Expected: i32, Found: ImmutableString")),
605            NineSlice(_) => Err(WrongType("Expected: i32, Found: NineI8")),
606        }
607    }
608}
609
610impl TryFrom<TileSetPropertyValue> for f32 {
611    type Error = TilePropertyError;
612
613    fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
614        use TilePropertyError::*;
615        use TileSetPropertyValue::*;
616        match value {
617            I32(_) => Err(WrongType("Expected: f32, Found: i32")),
618            F32(v) => Ok(v),
619            String(_) => Err(WrongType("Expected: f32, Found: ImmutableString")),
620            NineSlice(_) => Err(WrongType("Expected: f32, Found: NineI8")),
621        }
622    }
623}
624
625impl TryFrom<TileSetPropertyValue> for ImmutableString {
626    type Error = TilePropertyError;
627
628    fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
629        use TilePropertyError::*;
630        use TileSetPropertyValue::*;
631        match value {
632            I32(_) => Err(WrongType("Expected: ImmutableString, Found: i32")),
633            F32(_) => Err(WrongType("Expected: ImmutableString, Found: f32")),
634            String(v) => Ok(v),
635            NineSlice(_) => Err(WrongType("Expected: ImmutableString, Found: NineI8")),
636        }
637    }
638}
639
640impl TryFrom<TileSetPropertyValue> for NineI8 {
641    type Error = TilePropertyError;
642
643    fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
644        use TilePropertyError::*;
645        use TileSetPropertyValue::*;
646        match value {
647            I32(_) => Err(WrongType("Expected: NineI8, Found: i32")),
648            F32(_) => Err(WrongType("Expected: NineI8, Found: f32")),
649            String(_) => Err(WrongType("Expected: NineI8, Found: ImmutableString")),
650            NineSlice(v) => Ok(v),
651        }
652    }
653}
654
655impl From<TileSetPropertyValue> for TileSetPropertyOptionValue {
656    fn from(value: TileSetPropertyValue) -> Self {
657        use TileSetPropertyOptionValue as OValue;
658        use TileSetPropertyValue as Value;
659        match value {
660            Value::I32(x) => OValue::I32(Some(x)),
661            Value::F32(x) => OValue::F32(Some(x)),
662            Value::String(x) => OValue::String(Some(x)),
663            Value::NineSlice(arr) => OValue::NineSlice(arr.0.map(Some)),
664        }
665    }
666}
667
668impl From<TileSetPropertyOptionValue> for TileSetPropertyValue {
669    fn from(value: TileSetPropertyOptionValue) -> Self {
670        use TileSetPropertyOptionValue as OValue;
671        use TileSetPropertyValue as Value;
672        match value {
673            OValue::I32(x) => Value::I32(x.unwrap_or_default()),
674            OValue::F32(x) => Value::F32(x.unwrap_or_default()),
675            OValue::String(x) => Value::String(x.unwrap_or_default()),
676            OValue::NineSlice(arr) => Value::NineSlice(NineI8(arr.map(Option::unwrap_or_default))),
677        }
678    }
679}
680
681impl TileSetPropertyOptionValue {
682    /// Combines this value with the given value, replacing the content of this value with None
683    /// wherever it differs from the given value.
684    pub fn intersect(&mut self, value: &TileSetPropertyValue) {
685        use TileSetPropertyOptionValue as OptValue;
686        use TileSetPropertyValue as PropValue;
687        match self {
688            OptValue::I32(x0) => {
689                if let Some(x) = x0 {
690                    if *value != PropValue::I32(*x) {
691                        *x0 = None
692                    }
693                }
694            }
695            OptValue::F32(x0) => {
696                if let Some(x) = x0 {
697                    if *value != PropValue::F32(*x) {
698                        *x0 = None
699                    }
700                }
701            }
702            OptValue::String(x0) => {
703                if let Some(x) = x0 {
704                    if let PropValue::String(x1) = value {
705                        if *x != *x1 {
706                            *x0 = None
707                        }
708                    } else {
709                        *x0 = None
710                    }
711                }
712            }
713            OptValue::NineSlice(arr0) => {
714                if let PropValue::NineSlice(arr1) = value {
715                    for (x0, x1) in arr0.iter_mut().zip(arr1.0.iter()) {
716                        if let Some(x) = x0 {
717                            if *x != *x1 {
718                                *x0 = None
719                            }
720                        }
721                    }
722                } else {
723                    *arr0 = [None; 9];
724                }
725            }
726        }
727    }
728}