From 6acbc621adb674a32bfca598f6f802725e883207 Mon Sep 17 00:00:00 2001 From: uurha Date: Tue, 27 Feb 2024 13:03:56 +0000 Subject: [PATCH] Add SerializableDictionary.cs and SerializedType.cs --- Runtime/Properties/ReactiveProperty.cs | 60 ++++- .../Properties/ReadOnlyReactiveProperty.cs | 46 ++++ .../ReadOnlyReactiveProperty.cs.meta | 3 + Runtime/Ranges/Range.cs | 94 ++++++-- Runtime/Ranges/RangeExtensions.cs | 68 +++++- Runtime/SerializedTypes.meta | 8 + .../SerializedTypes/SerializedDictionary.cs | 213 ++++++++++++++++++ .../SerializedDictionary.cs.meta | 3 + Runtime/SerializedTypes/SerializedType.cs | 123 ++++++++++ .../SerializedTypes/SerializedType.cs.meta | 11 + Runtime/Tree/INodeValue.cs | 15 ++ Runtime/Tree/INodeValue.cs.meta | 3 + Runtime/Tree/TreeNode.cs | 56 ++++- package.json | 4 +- 14 files changed, 675 insertions(+), 32 deletions(-) create mode 100644 Runtime/Properties/ReadOnlyReactiveProperty.cs create mode 100644 Runtime/Properties/ReadOnlyReactiveProperty.cs.meta create mode 100644 Runtime/SerializedTypes.meta create mode 100644 Runtime/SerializedTypes/SerializedDictionary.cs create mode 100644 Runtime/SerializedTypes/SerializedDictionary.cs.meta create mode 100644 Runtime/SerializedTypes/SerializedType.cs create mode 100644 Runtime/SerializedTypes/SerializedType.cs.meta create mode 100644 Runtime/Tree/INodeValue.cs create mode 100644 Runtime/Tree/INodeValue.cs.meta diff --git a/Runtime/Properties/ReactiveProperty.cs b/Runtime/Properties/ReactiveProperty.cs index 784955c..20fd656 100644 --- a/Runtime/Properties/ReactiveProperty.cs +++ b/Runtime/Properties/ReactiveProperty.cs @@ -3,14 +3,21 @@ namespace Better.DataStructures.Properties { + /// + /// A reactive property that notifies subscribers about changes to its value. + /// + /// The type of the value. [Serializable] public class ReactiveProperty { - public event Action ValueChangedEvent; + private event Action ValueChangedEvent; + private ReadOnlyReactiveProperty _cachedReadOnly; - [SerializeField] - protected T _value; + [SerializeField] protected T _value; + /// + /// Gets or sets the value of the property. Setting the value notifies all subscribers about the change. + /// public T Value { get => _value; @@ -21,14 +28,59 @@ public T Value } } + /// + /// Initializes a new instance of the ReactiveProperty class with an optional default value. + /// + /// The initial value of the property. public ReactiveProperty(T defaultValue = default) { _value = defaultValue; } + /// + /// Notifies subscribers about a change to the property's value. + /// public virtual void SetDirty() { ValueChangedEvent?.Invoke(Value); } + + /// + /// Subscribes a callback action to be invoked when the property's value changes. + /// + /// The callback action to invoke on value change. + public virtual void Subscribe(Action action) + { + ValueChangedEvent += action; + } + + /// + /// Subscribes a callback action to be invoked immediately and also when the property's value changes in the future. + /// + /// The callback action to invoke. + public virtual void SubscribeWithInvoke(Action action) + { + Subscribe(action); + action?.Invoke(Value); + } + + /// + /// Unsubscribes a previously subscribed callback action from being invoked when the property's value changes. + /// + /// The callback action to unsubscribe. + public virtual void Unsubscribe(Action action) + { + ValueChangedEvent -= action; + } + + /// + /// Gets a read-only version of the ReactiveProperty, which can be exposed publicly to prevent external modifications. + /// + /// A ReadOnlyReactiveProperty encapsulating the read-only view of this ReactiveProperty. + public ReadOnlyReactiveProperty AsReadOnly() + { + _cachedReadOnly ??= new ReadOnlyReactiveProperty(this); + return _cachedReadOnly; + } } -} \ No newline at end of file +} diff --git a/Runtime/Properties/ReadOnlyReactiveProperty.cs b/Runtime/Properties/ReadOnlyReactiveProperty.cs new file mode 100644 index 0000000..27c31bc --- /dev/null +++ b/Runtime/Properties/ReadOnlyReactiveProperty.cs @@ -0,0 +1,46 @@ +using System; + +namespace Better.DataStructures.Properties +{ + /// + /// Represents a read-only wrapper around a ReactiveProperty, allowing subscription to value changes without the ability to modify the value. + /// + /// The type of the value. + [Serializable] + public sealed class ReadOnlyReactiveProperty + { + private readonly ReactiveProperty _source; + + /// + /// Gets the current value of the underlying ReactiveProperty. + /// + public T Value => _source.Value; + + /// + /// Initializes a new instance of the ReadOnlyReactiveProperty class with a ReactiveProperty as its source. + /// + /// The ReactiveProperty to encapsulate in a read-only manner. + public ReadOnlyReactiveProperty(ReactiveProperty source) + { + _source = source; + } + + /// + /// Subscribes a callback action to be invoked when the underlying ReactiveProperty's value changes. + /// + /// The callback action to invoke on value change. + public void Subscribe(Action action) => _source.Subscribe(action); + + /// + /// Subscribes a callback action to be invoked immediately with the current value, and also when the underlying ReactiveProperty's value changes in the future. + /// + /// The callback action to invoke. + public void SubscribeWithInvoke(Action action) => _source.SubscribeWithInvoke(action); + + /// + /// Unsubscribes a previously subscribed callback action from being invoked when the underlying ReactiveProperty's value changes. + /// + /// The callback action to unsubscribe. + public void Unsubscribe(Action action) => _source.Unsubscribe(action); + } +} diff --git a/Runtime/Properties/ReadOnlyReactiveProperty.cs.meta b/Runtime/Properties/ReadOnlyReactiveProperty.cs.meta new file mode 100644 index 0000000..117b65c --- /dev/null +++ b/Runtime/Properties/ReadOnlyReactiveProperty.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 359fbe56483049da8a92ec287a543e0b +timeCreated: 1708237851 \ No newline at end of file diff --git a/Runtime/Ranges/Range.cs b/Runtime/Ranges/Range.cs index 084632f..4ea144d 100644 --- a/Runtime/Ranges/Range.cs +++ b/Runtime/Ranges/Range.cs @@ -1,62 +1,124 @@ using System; using System.Collections.Generic; using UnityEngine; +using UnityEngine.Serialization; namespace Better.DataStructures.Ranges { + /// + /// Represents a range with minimum and maximum values of a generic type. + /// + /// The type of the values defining the range. [Serializable] public class Range : IEquatable> { - [SerializeField] private T min; - [SerializeField] private T max; + [FormerlySerializedAs("min")] [SerializeField] private T _min; + [FormerlySerializedAs("max")] [SerializeField] private T _max; + /// + /// Initializes a new instance of the Range class with default minimum and maximum values. + /// public Range() { - min = default; - max = default; + _min = default; + _max = default; } + /// + /// Initializes a new instance of the Range class by copying another range. + /// + /// The range to copy. + public Range(Range range) + { + _min = range.Min; + _max = range.Max; + } + + /// + /// Initializes a new instance of the Range class with specified minimum and maximum values. + /// + /// The minimum value of the range. + /// The maximum value of the range. public Range(T min, T max) { - this.min = min; - this.max = max; + _min = min; + _max = max; } - public T Min => min; + /// + /// Gets the minimum value of the range. + /// + public T Min => _min; - public T Max => max; + /// + /// Gets the maximum value of the range. + /// + public T Max => _max; + /// + /// Determines whether the specified Range is equal to the current Range. + /// + /// The Range to compare with the current Range. + /// true if the specified Range is equal to the current Range; otherwise, false. public bool Equals(Range other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return EqualityComparer.Default.Equals(min, other.min) && EqualityComparer.Default.Equals(max, other.max); + return EqualityComparer.Default.Equals(_min, other._min) && EqualityComparer.Default.Equals(_max, other._max); } + /// + /// Determines whether the specified object is equal to the current Range. + /// + /// The object to compare with the current Range. + /// true if the specified object is equal to the current Range; otherwise, false. public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; + if (obj.GetType() != GetType()) return false; return Equals((Range)obj); } - public Range UpdateMax(T maxValue) + /// + /// Creates a new instance of the Range that is a copy of the current Range. + /// + /// A new Range instance that is a copy of this Range. + public Range Copy() + { + return new Range(_min, _max); + } + + /// + /// Creates a new instance of the Range with the same minimum value as this instance and a new maximum value. + /// + /// The new maximum value for the range. + /// A new Range instance with the updated maximum value while retaining the original minimum value. + public Range CopyWithMax(T maxValue) { - return new Range(min, maxValue); + return new Range(_min, maxValue); } - public Range UpdateMin(T minValue) + /// + /// Creates a new instance of the Range with the same maximum value as this instance and a new minimum value. + /// + /// The new minimum value for the range. + /// A new Range instance with the updated minimum value while retaining the original maximum value. + public Range CopyWithMin(T minValue) { - return new Range(minValue, max); + return new Range(minValue, _max); } + /// + /// Serves as the default hash function. + /// + /// A hash code for the current Range. public override int GetHashCode() { unchecked { - return (EqualityComparer.Default.GetHashCode(min) * 397) ^ EqualityComparer.Default.GetHashCode(max); + return (EqualityComparer.Default.GetHashCode(_min) * 397) ^ EqualityComparer.Default.GetHashCode(_max); } } } -} \ No newline at end of file +} diff --git a/Runtime/Ranges/RangeExtensions.cs b/Runtime/Ranges/RangeExtensions.cs index 3183510..5ab484c 100644 --- a/Runtime/Ranges/RangeExtensions.cs +++ b/Runtime/Ranges/RangeExtensions.cs @@ -3,52 +3,106 @@ namespace Better.DataStructures.Ranges { + /// + /// Provides extension methods for Range of different types to perform common operations. + /// public static class RangeExtensions { + /// + /// Clamps an integer value to the range defined by the Range object. + /// + /// The Range object. + /// The value to clamp. + /// The clamped value. public static int Clamp(this Range range, int value) { return Mathf.Clamp(value, range.Min, range.Max); } + /// + /// Clamps a float value to the range defined by the Range object. + /// + /// The Range object. + /// The value to clamp. + /// The clamped value. public static float Clamp(this Range range, float value) { return Mathf.Clamp(value, range.Min, range.Max); } - + + /// + /// Returns a random float value within the range defined by the Range object. + /// + /// The Range object. + /// A random float value within the range. public static float Random(this Range range) { return UnityEngine.Random.Range(range.Min, range.Max); } + /// + /// Returns a random integer value within the range defined by the Range object. + /// + /// The Range object. + /// A random integer value within the range. public static int Random(this Range range) { - return UnityEngine.Random.Range(range.Min, range.Max + 1); // +1 to have inclusive random + return UnityEngine.Random.Range(range.Min, range.Max + 1); // +1 to include the maximum value in the range. } + /// + /// Linearly interpolates between the minimum and maximum values of the range based on the given interpolation parameter. + /// + /// The Range object. + /// The interpolation parameter. + /// The interpolated float value. public static float Lerp(this Range range, float t) { return Mathf.Lerp(range.Min, range.Max, t); } + /// + /// Linearly interpolates between the minimum and maximum values of the range based on the given interpolation parameter and rounds the result to the nearest integer. + /// + /// The Range object. + /// The interpolation parameter. + /// The interpolated and rounded integer value. public static int Lerp(this Range range, float t) { return Mathf.RoundToInt(Mathf.Lerp(range.Min, range.Max, t)); } + /// + /// Checks if a float value is contained within the range defined by the Range object. + /// + /// The Range object. + /// The value to check. + /// True if the value is within the range; otherwise, false. public static bool Contains(this Range range, float value) { return value >= range.Min && value <= range.Max; } + /// + /// Checks if an integer value is contained within the range defined by the Range object. + /// + /// The Range object. + /// The value to check. + /// True if the value is within the range; otherwise, false. public static bool Contains(this Range range, int value) { return value >= range.Min && value <= range.Max; } + /// + /// Clamps a double value to the range defined by the Range object. Uses Math.Clamp if available. + /// + /// The Range object. + /// The value to clamp. + /// The clamped value. public static double Clamp(this Range range, double value) { #if UNITY_2021_3_OR_NEWER - return Math.Clamp(value, range.Min, range.Max); #else if (value < range.Min) @@ -59,6 +113,12 @@ public static double Clamp(this Range range, double value) #endif } + /// + /// Clamps a byte value to the range defined by the Range object. Uses Math.Clamp if available. + /// + /// The Range object. + /// The value to clamp. + /// The clamped value. public static byte Clamp(this Range range, byte value) { #if UNITY_2021_3_OR_NEWER @@ -72,4 +132,4 @@ public static byte Clamp(this Range range, byte value) #endif } } -} \ No newline at end of file +} diff --git a/Runtime/SerializedTypes.meta b/Runtime/SerializedTypes.meta new file mode 100644 index 0000000..de6dfaa --- /dev/null +++ b/Runtime/SerializedTypes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2ce7ca8a66b95b04d80cabaf3f90c304 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/SerializedTypes/SerializedDictionary.cs b/Runtime/SerializedTypes/SerializedDictionary.cs new file mode 100644 index 0000000..19f87ed --- /dev/null +++ b/Runtime/SerializedTypes/SerializedDictionary.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Better.DataStructures.SerializedTypes +{ + /// + /// A serializable dictionary that can be used with Unity's serialization system. + /// + /// The type of the keys in the dictionary. + /// The type of the values in the dictionary. + [Serializable] + public class SerializedDictionary : IDictionary, ISerializationCallbackReceiver, IReadOnlyDictionary + { + [SerializeField] private List _keys; // Serialized list of keys + [SerializeField] private List _values; // Serialized list of values + + private Dictionary _dictionary = new(); // The underlying dictionary + + /// + /// Gets or sets the value associated with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. + public TValue this[TKey key] + { + get => _dictionary[key]; + set => _dictionary[key] = value; + } + + /// + /// Readonly collection of dictionary keys. + /// + IEnumerable IReadOnlyDictionary.Keys => Keys; + + /// + /// Readonly collection of dictionary values. + /// + IEnumerable IReadOnlyDictionary.Values => Values; + + /// + /// Gets an ICollection containing the keys of the Dictionary. + /// + public ICollection Keys => _dictionary.Keys; + + /// + /// Gets an ICollection containing the values of the Dictionary. + /// + public ICollection Values => _dictionary.Values; + + /// + /// Gets the number of key/value pairs contained in the Dictionary. + /// + public int Count => _dictionary.Count; + + /// + /// Gets a value indicating whether the Dictionary is read-only. + /// + bool ICollection>.IsReadOnly => (_dictionary as ICollection>).IsReadOnly; + + /// + /// Gets the key associated with the specified value. + /// + /// The value to find the key of. + /// The key associated with the specified value. + public TKey this[TValue value] + { + get + { + _keys = new List(_dictionary.Keys); + _values = new List(_dictionary.Values); + var index = _values.FindIndex(x => EqualityComparer.Default.Equals(x, value)); + + if (index < 0) + { + throw new KeyNotFoundException(); + } + + return _keys[index]; + } + } + + /// + /// Adds the specified key and value to the dictionary. + /// + /// The key of the element to add. + /// The value of the element to add. + public void Add(TKey key, TValue value) + { + _dictionary.Add(key, value); + } + + /// + /// Determines whether the Dictionary contains the specified key. + /// + /// The key to locate in the Dictionary. + /// true if the Dictionary contains an element with the specified key; otherwise, false. + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + /// + /// Removes the value with the specified key from the Dictionary. + /// + /// The key of the element to remove. + /// true if the element is successfully removed; otherwise, false. + public bool Remove(TKey key) + { + return _dictionary.Remove(key); + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. + /// true if the object that implements IDictionary contains an element with the specified key; otherwise, false. + public bool TryGetValue(TKey key, out TValue value) + { + return _dictionary.TryGetValue(key, out value); + } + + /// + /// Removes all keys and values from the Dictionary. + /// + public void Clear() + { + _dictionary.Clear(); + } + + /// + /// Adds a key/value pair to the ICollection. + /// + /// The key/value pair to add to the ICollection. + void ICollection>.Add(KeyValuePair item) + { + (_dictionary as ICollection>).Add(item); + } + + /// + /// Determines whether the ICollection contains a specific value. + /// + /// The object to locate in the ICollection. + /// true if item is found in the ICollection; otherwise, false. + bool ICollection>.Contains(KeyValuePair item) + { + return (_dictionary as ICollection>).Contains(item); + } + + /// + /// Copies the elements of the ICollection to an Array, starting at a particular Array index. + /// + /// The one-dimensional Array that is the destination of the elements copied from ICollection. + /// The zero-based index in array at which copying begins. + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + (_dictionary as ICollection>).CopyTo(array, arrayIndex); + } + + /// + /// Removes the first occurrence of a specific object from the ICollection. + /// + /// The object to remove from the ICollection. + /// true if item was successfully removed from the ICollection; otherwise, false. + bool ICollection>.Remove(KeyValuePair item) + { + return (_dictionary as ICollection>).Remove(item); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// An enumerator that can be used to iterate through the collection. + IEnumerator> IEnumerable>.GetEnumerator() + { + return (_dictionary as IEnumerable>).GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// An IEnumerator object that can be used to iterate through the collection. + public IEnumerator GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + /// + /// Called before Unity serializes this object. + /// + void ISerializationCallbackReceiver.OnBeforeSerialize() + { + _keys = new List(_dictionary.Keys); + _values = new List(_dictionary.Values); + } + + /// + /// Called after Unity deserializes this object. + /// + void ISerializationCallbackReceiver.OnAfterDeserialize() + { + Debug.Assert(_keys.Count == _values.Count); + Clear(); + + for (var i = 0; i < _keys.Count; ++i) + { + Add(_keys[i], _values[i]); + } + } + } +} \ No newline at end of file diff --git a/Runtime/SerializedTypes/SerializedDictionary.cs.meta b/Runtime/SerializedTypes/SerializedDictionary.cs.meta new file mode 100644 index 0000000..99f03ac --- /dev/null +++ b/Runtime/SerializedTypes/SerializedDictionary.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c6dfcb41a6c04fa3a6a1b38006ee437b +timeCreated: 1708904004 \ No newline at end of file diff --git a/Runtime/SerializedTypes/SerializedType.cs b/Runtime/SerializedTypes/SerializedType.cs new file mode 100644 index 0000000..9f4a2b0 --- /dev/null +++ b/Runtime/SerializedTypes/SerializedType.cs @@ -0,0 +1,123 @@ +using System; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Better.DataStructures.SerializedTypes +{ + /// + /// Enables the serialization of a System.Type reference within Unity, storing the type's assembly qualified name. + /// + [Serializable] + public class SerializedType : ISerializationCallbackReceiver + { + [FormerlySerializedAs("fullQualifiedName")] + [SerializeField] private protected string _fullQualifiedName; // The full name of the type, used for serialization. + + private Type _type; // The actual Type object, not serialized. + + /// + /// Gets the Type object associated with this SerializedType. Attempts to resolve the Type if not already cached. + /// + public Type Type + { + get + { + if (_type == null && !string.IsNullOrEmpty(_fullQualifiedName)) + { + if (!TryGetReferenceType(_fullQualifiedName, out _type)) + { + _fullQualifiedName = string.Empty; + } + } + return _type; + } + } + + /// + /// Default constructor for . + /// + public SerializedType() + { + } + + /// + /// Initializes a new instance of with the specified type name. + /// + /// The assembly qualified name of the type. + public SerializedType(string qualifiedTypeName) + { + ValidateStringType(qualifiedTypeName); + _fullQualifiedName = qualifiedTypeName; + } + + /// + /// Validates the provided type name and initializes the Type object if it's valid. + /// + /// The assembly qualified name to validate. + private protected void ValidateStringType(string qualifiedTypeName) + { + if (!TryGetReferenceType(qualifiedTypeName, out _type)) + { + Debug.LogWarning($"{qualifiedTypeName} not found"); + } + } + + /// + /// Initializes a new instance of with the specified Type object. + /// + /// The Type object to serialize. + public SerializedType(Type type) + { + _type = type; + _fullQualifiedName = type.AssemblyQualifiedName; + } + + private static bool TryGetReferenceType(string value, out Type type) + { + type = !string.IsNullOrEmpty(value) ? Type.GetType(value) : null; + return type != null; + } + + /// + /// Returns a string representation of the Type, or "(None)" if the type is not set. + /// + /// A string representing the Type. + public override string ToString() + { + return _type != null ? _type.FullName : "(None)"; + } + + /// + /// Called after Unity deserializes this object. Attempts to resolve the Type from its serialized name. + /// + void ISerializationCallbackReceiver.OnAfterDeserialize() + { + if (!string.IsNullOrEmpty(_fullQualifiedName)) + { + _type = Type.GetType(_fullQualifiedName); + if (_type == null) + { +#if UNITY_EDITOR + Debug.LogWarning($"'{_fullQualifiedName}' class type not found."); +#endif + } + } + else + { + _type = null; + } + } + + /// + /// Called before Unity serializes this object. No implementation needed for this class. + /// + void ISerializationCallbackReceiver.OnBeforeSerialize() + { + } + + // Implicit conversions to and from Type and string representations. + public static implicit operator string(SerializedType typeReference) => typeReference._fullQualifiedName; + public static implicit operator Type(SerializedType typeReference) => typeReference.Type; + public static implicit operator SerializedType(Type type) => new SerializedType(type); + } +} diff --git a/Runtime/SerializedTypes/SerializedType.cs.meta b/Runtime/SerializedTypes/SerializedType.cs.meta new file mode 100644 index 0000000..d17928c --- /dev/null +++ b/Runtime/SerializedTypes/SerializedType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb477dbb43740c14a955180817a4e9aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Tree/INodeValue.cs b/Runtime/Tree/INodeValue.cs new file mode 100644 index 0000000..d3e8915 --- /dev/null +++ b/Runtime/Tree/INodeValue.cs @@ -0,0 +1,15 @@ +namespace Better.DataStructures.Tree +{ + /// + /// Defines a contract for values stored in tree nodes that need to be aware of their corresponding tree node. + /// + /// The type of the value stored in the tree node. + public interface INodeValue + { + /// + /// Sets the TreeNode that this value is associated with. + /// + /// The TreeNode object that this value is contained within. + void SetNode(TreeNode node); + } +} \ No newline at end of file diff --git a/Runtime/Tree/INodeValue.cs.meta b/Runtime/Tree/INodeValue.cs.meta new file mode 100644 index 0000000..09dfd9e --- /dev/null +++ b/Runtime/Tree/INodeValue.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f2193aca6823430884120ef808f96739 +timeCreated: 1708859063 \ No newline at end of file diff --git a/Runtime/Tree/TreeNode.cs b/Runtime/Tree/TreeNode.cs index 8d7de26..c44d65c 100644 --- a/Runtime/Tree/TreeNode.cs +++ b/Runtime/Tree/TreeNode.cs @@ -5,34 +5,60 @@ namespace Better.DataStructures.Tree { - public interface INodeValue - { - public void SetNode(TreeNode node); - } - + /// + /// Represents a node in a tree structure with a value and a list of child nodes. + /// + /// The type of the value stored in the node. public class TreeNode { + // Holds the value of the node private protected readonly T _value; + + // List to hold child nodes private readonly List> _children = new List>(); + /// + /// Initializes a new instance of the TreeNode class with the specified value. + /// + /// The value to store in the node. public TreeNode(T value) { _value = value; } + /// + /// Gets the child node at the specified index. + /// + /// The zero-based index of the child node to get. + /// The child node at the specified index. public TreeNode this[int i] => _children[i]; + /// + /// Gets the parent node of this node. + /// public TreeNode Parent { get; private set; } + /// + /// Gets the value stored in the node. + /// public T Value => _value; + /// + /// Gets a read-only collection of the node's children. + /// public ReadOnlyCollection> Children => _children.AsReadOnly(); + /// + /// Adds a new child node with the specified value to this node. + /// + /// The value of the new child node. + /// The newly created child node. public TreeNode AddChild(T value) { var node = new TreeNode(value) { Parent = this }; if (value is INodeValue nodeValue) { + // If the value implements INodeValue, set its node reference nodeValue.SetNode(node); } @@ -40,16 +66,30 @@ public TreeNode AddChild(T value) return node; } + /// + /// Adds new child nodes with the specified values to this node. + /// + /// The values for the new child nodes. + /// An array of the newly created child nodes. public TreeNode[] AddChildren(params T[] values) { return values.Select(AddChild).ToArray(); } + /// + /// Removes the specified child node from this node. + /// + /// The child node to remove. + /// true if the node was successfully removed; otherwise, false. public bool RemoveChild(TreeNode node) { return _children.Remove(node); } + /// + /// Performs the specified action on the value of this node and recursively on the values of all descendant nodes. + /// + /// The action to perform on each value. public void Traverse(Action action) { action(Value); @@ -57,9 +97,13 @@ public void Traverse(Action action) child.Traverse(action); } + /// + /// Returns an enumerable collection of the values from this node and all descendant nodes in depth-first order. + /// + /// An enumerable collection of values. public IEnumerable Flatten() { return new[] { Value }.Concat(_children.SelectMany(x => x.Flatten())); } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 9aa755b..1cfcca1 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "com.uurha.betterdatastructures", "displayName": "Better Data Structures", "description": "Collection of useful data structures for Unity", - "version": "0.1.2", - "unity": "2018.3", + "version": "0.1.5", + "unity": "2021.3", "author": { "name": "Better Plugins", "url": "https://github.com/techno-dwarf-works/"