/// <summary> /// Simple copy constructor that assumes the structure of the existing KD Tree is /// intact /// </summary> /// <param name="_toCopy">The KD Tree to copy</param> private void CopyConstructor(KDTree <T> _toCopy) { m_axisCount = _toCopy.m_axisCount; m_selector = _toCopy.m_selector; m_count = _toCopy.m_count; m_distanceFunction = _toCopy.m_distanceFunction; m_tree = _toCopy.m_tree.Clone(); }
/// <summary> /// Construct a new K-D Tree, using the default selector for objects. This is a /// performance-hindered version, as it relies on /// <see cref="IMultiDimensionalPoint"/> . /// </summary> public KDTree() { m_distanceFunction = GetMultiDimensionalDistance; if (!IsTMultiDimensional()) { throw new InvalidOperationException("The underlying type " + typeof(T).ToString() + " does not implement IMultidimensionalPoint and no selector was specified."); } m_tree = new SparseLeafBinaryTree <T>(1023); }
/// <summary> /// Construct a new K-D Tree, using the specified selector for objects /// </summary> public KDTree(int _axisCount, Func <T, int, double> _selector, Func <T, T, double> _distanceFunction = null) { if (_axisCount < 1) { throw new ArgumentOutOfRangeException("Axis Count", _axisCount, "Axis Count must be greater than 1"); } m_selector = _selector ?? throw new ArgumentNullException("_selector", "The selector cannot be null"); m_axisCount = _axisCount; m_distanceFunction = _distanceFunction ?? GetMultiDimensionalDistance; m_tree = new SparseLeafBinaryTree <T>(1023); }
/// <summary> /// Create a shallow copy of this binary tree /// </summary> /// <returns></returns> public SparseLeafBinaryTree <T> Clone() { var clone = new SparseLeafBinaryTree <T>(m_root.Length) { m_root = (T[])m_root.Clone() }; foreach (var kv in m_leaves) { clone.m_leaves[kv.Key] = kv.Value; } return(clone); }
/// <summary> /// Set up the K-D Tree based on an enumeration of elements. If the enumeration is /// another KDTree of type T, then a copy constructor <see cref="Clone"/> will be /// used. /// </summary> /// <param name="_collection">The collection of objects</param> /// <param name="_axisCount"> /// The number of dimensions present in this KD Tree... use default if T implements /// <see cref="IMultiDimensionalPoint"/> /// </param> /// <param name="_selector"> /// The selector to use on the objects- if NULL, relies on the objects to implement /// <see cref="IMultiDimensionalPoint"/> /// </param> /// <param name="_distanceFunction"> /// A function that determines the distance between two nodes in the tree. Assumes /// Distances are measured using double precision floating point scalar values. /// </param> /// <remarks> /// If _axisCount == -1 and/or _selector == null, then T MUST implement /// <see cref="IMultiDimensionalPoint"/> . /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown if _axisCount is less than 1 AND T is not an implementation of /// ArgumentOutOfRangeException /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if _selector is NULL AND T is not an implementation of /// ArgumentOutOfRangeException /// </exception> /// <exception cref="ArgumentException"> /// Thrown if _axisCount is less than 1 AND there are no elements in the /// collection, even if T implements <see cref="IMultiDimensionalPoint"/> . /// </exception> public KDTree(IEnumerable <T> _collection, int _axisCount = -1, Func <T, int, double> _selector = null, Func <T, T, double> _distanceFunction = null) { if (_collection is KDTree <T> ) // we know the internal storage is already a KD Tree- simply copy it { CopyConstructor(_collection as KDTree <T>); return; } m_distanceFunction = _distanceFunction ?? GetMultiDimensionalDistance; if (!IsTMultiDimensional()) { if (_axisCount < 1) { throw new ArgumentOutOfRangeException("Axis Count", _axisCount, "Axis Count must be at least 1"); } if (_selector == null) { throw new ArgumentNullException("_selector", "The selector cannot be null if T doesn't inherit IMultiDimensionalPoint"); } } if (_axisCount > 0) { m_axisCount = _axisCount; } if (_selector != null) { m_selector = _selector; } var array = _collection.ToArray(); // Construct using an array m_tree = new SparseLeafBinaryTree <T>(array.Length); m_count = array.Length; if (m_count > 0) { if (m_axisCount < 1) { m_axisCount = (array[0] as IMultiDimensionalPoint).GetAxisCount(); } BuildComparers(); ConstructFromArray(array, 0, array.Length - 1, 0, 0); } if (m_axisCount < 1) { throw new ArgumentException("May not specify an empty collection when an explicit Axis Count has not been specified"); } }
/// <summary> /// Use this method when a significant number of nodes have been added to the tree /// after its construction. This has the effect of re-balancing the tree. This is /// not a cheap operation - O(n log n) /// </summary> public void RebuildTree(int _capacity) { var items = new T[m_count]; var idx = 0; foreach (var item in this) { items[idx++] = item; } m_tree = new SparseLeafBinaryTree <T>(_capacity); BuildComparers(); ConstructFromArray(items, 0, items.Length - 1, 0, 0); }